#include "Renderer.h"

#include "ds3d.h"

#define LIGHT 72

extern uint8 font[];

static char *messages[]=
{
	"  \240\241\242\243\244\245\246 Playable Demo     \211\212",
	" Distributed by: M. Andrzejak ",
	"     Nintendo DS port by:     ",
	"      WAHa.06x36 & coda       ",
	//" [mandrzej@et.put.poznan.pl]  ",
	"Go through the gates 1-2-3-4-1",
	"      as fast as you can !    ",
	"My best time is 10:16 !       ",
	//"Full game will be             ",
	//"available soon.               ",
	"                              ",
	"\224\225\221time \226\227\221 go to this gate   ",
	"Controls: \221\220 turn             ",
	"\222\223 forward & backward         ",
	"[A] or [L] stoop and jump     ",
	"[B] or [R] speedup            ",
	//"[F1][F2][F3] - screen size    ",
	//"[S] 3D Stereo \213\214\215\216            ",
	//"\213\214\215\216 left:green, right:red    ",
	//"[O] distance \230\231 \230\231 in \213\214\215\216 3D ",
	//"[B] brightness balance in 3D  ",
	//"[D] or wait 30 sec.  -  DEMO  ",
	"[START] back to start         ",
	//"[Esc] exit                    ",
	"           Music:             ",
	"  *** Stardust memories ***   ",
	"  composed by jester/sanity   ",
	"                              ",
};

uint16 palette[256]=
{
	0x0000,0x0842,0x1084,0x18c6,0x2108,0x294a,0x318c,0x39ce,0x4631,0x4e73,0x56b5,0x5ef7,0x6739,0x6f7b,0x77bd,0x7fff,
	0x421f,0x211f,0x001f,0x0015,0x000a,0x090c,0x0d4f,0x1992,0x21d4,0x2e17,0x3e5a,0x4edd,0x5f3f,0x11a0,0x0ec0,0x03e0,
	0x43f0,0x2800,0x5400,0x7c00,0x7d4a,0x7eb5,0x035d,0x02fa,0x0297,0x0253,0x01f0,0x5eb1,0x526f,0x460d,0x0a20,0x7e3f,
	0x71fb,0x65b8,0x5595,0x4952,0x494e,0x494b,0x516c,0x598d,0x61af,0x6210,0x6252,0x6294,0x6314,0x5b14,0x5315,0x5318,
	0x46d7,0x3ab5,0x2e73,0x2231,0x25d1,0x2571,0x2531,0x2533,0x2536,0x2118,0x1cfa,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,
	0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,
	0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,
	0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,

	0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,
	0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,
	0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,
	0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,
	0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,
	0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,
	0x38e0,0x7fff,0x03e0,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,
	0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x03ff,0x7fff,0x0220,0x03e0,0x0010,0x001f,
};

uint8 paletteremap1[128]=
{
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,3,4,5,6,7,8,
};

uint8 paletteremap2[16]={ 0,225,226,250,251,252,253,254,255,0,0,0,0,0,0,0 };

void SetHighlightBar(uint32 level)
{
	for(int i=0;i<=8;i++)
	{
		BG_PALETTE_SUB[16+i]=i*(18+mulf32(level,13))/8;
	}
}

int InitRenderer(Renderer *renderer)
{
	// 3D on main controller

	videoSetMode(MODE_0_3D);

	vramSetBankA(VRAM_A_TEXTURE);
	vramSetBankB(VRAM_B_TEXTURE);
	vramSetBankC(VRAM_C_TEXTURE);
//	vramSetBankD(VRAM_D_TEXTURE);

	DSInit3D();
	DSClearParams(0,0,0,31,63);
	DSClearDepth(0x7fff);
	DSViewport(0,0,255,191);

	vramSetBankE(VRAM_E_LCD);
	for(int i=0;i<256;i++) VRAM_E[i]=palette[i];
	for(int i=0;i<16;i++) VRAM_E[i+256]=palette[paletteremap2[i]];
	vramSetBankE(VRAM_E_TEX_PALETTE);

	renderer->palette=palette;

	for(int i=0;i<255;i++)
	{
		renderer->lighttable[i]=(4096*(LIGHT-i))/LIGHT;
	}

	// 2D on sub controller

	videoSetModeSub(MODE_0_2D|DISPLAY_BG0_ACTIVE|DISPLAY_BG1_ACTIVE);
	vramSetBankH(VRAM_H_SUB_BG);
	vramSetBankI(VRAM_I_SUB_BG);

	SUB_BG0_CR=(0<<2)|(8<<8); // charbase 0, 16 col, mapbase 16k, size 256x256
	SUB_BG1_CR=(2<<2)|(9<<8); // charbase 32k, 16 col, mapbase 18k, size 256x256

	BG_PALETTE_SUB[0]=0;
	BG_PALETTE_SUB[1]=0x7fff;
	SetHighlightBar(0);

	uint16 *vramptr=(uint16 *)0x6200000;
	for(int ty=0;ty<16;ty++)
	for(int tx=0;tx<16;tx++)
	for(int py=0;py<16;py++)
	for(int px=0;px<8;px+=4)
	{
		int byte=font[tx+(ty*16+py)*16];
		int a=(byte>>(7-px))&1;
		int b=(byte>>(6-px))&1;
		int c=(byte>>(5-px))&1;
		int d=(byte>>(4-px))&1;
		*vramptr++=(a<<0)|(b<<4)|(c<<8)|(d<<12);
	}

	vramptr=(uint16 *)0x6208020;
	for(int y=0;y<8;y++)
	for(int x=0;x<8;x+=4) *vramptr++=(y+1)*0x1111;

	for(int y=0;y<8;y++)
	for(int x=0;x<8;x+=4) *vramptr++=0x8888;

	for(int y=0;y<8;y++)
	for(int x=0;x<8;x+=4) *vramptr++=(8-y)*0x1111;

	vramptr=(uint16 *)0x6204800;
	for(int x=0;x<32;x++) *vramptr++=0x0000;
	for(int x=0;x<32;x++) *vramptr++=0x1001;
	for(int x=0;x<32;x++) *vramptr++=0x1002;
	for(int x=0;x<32;x++) *vramptr++=0x1002;
	for(int x=0;x<32;x++) *vramptr++=0x1002;
	for(int x=0;x<32;x++) *vramptr++=0x1002;
	for(int x=0;x<32;x++) *vramptr++=0x1002;
	for(int x=0;x<32;x++) *vramptr++=0x1003;

	return 1;
}

int TextureHasLight(uint8 *texdata)
{
	for(int i=0;i<128*128;i++) if(texdata[i]>=128) return 1;
	return 0;
}

int PrepareForMap(Renderer *renderer,Map *map)
{
	DSFreeAllTextures();

	renderer->whitetexture=DSMakeWhiteTexture();

	for(int i=0;i<NUM_MAP_TEXTURES;i++)
	{
		if(!map->texturedata[i]) continue;

		renderer->textures[i]=DSAllocAndCopyTexture(DS_TEX_FORMAT_PAL8|
		DS_TEX_SIZE_S_128|DS_TEX_SIZE_T_128|DS_TEX_WRAP_S|DS_TEX_WRAP_T,map->texturedata[i]);
if(renderer->textures[i]==DS_INVALID_TEXTURE) for(;;);

		if(TextureHasLight(map->texturedata[i]))
		{
			renderer->lights[i]=DSAllocTexture(DS_TEX_FORMAT_PAL4|DS_TEX_SIZE_S_128|
			DS_TEX_SIZE_T_128|DS_TEX_WRAP_S|DS_TEX_WRAP_T|DS_TEX_COLOR0_TRANS);

			uint32 vramtmp=vramSetMainBanks(VRAM_A_LCD,VRAM_B_LCD,VRAM_C_LCD,VRAM_D_LCD);

			uint8 *src=map->texturedata[i];
			uint16 *dest=DSTextureAddress(renderer->lights[i]);
if(renderer->lights[i]==DS_INVALID_TEXTURE) for(;;);
			for(int i=0;i<128*128/4;i++)
			{
				uint8 c1=*src++;
				uint8 c2=*src++;
				uint8 c3=*src++;
				uint8 c4=*src++;
				if(c1>=128) c1=paletteremap1[c1-128]; else c1=0;
				if(c2>=128) c2=paletteremap1[c2-128]; else c2=0;
				if(c3>=128) c3=paletteremap1[c3-128]; else c3=0;
				if(c4>=128) c4=paletteremap1[c4-128]; else c4=0;
				*dest++=(c4<<12)|(c3<<8)|(c2<<4)|c1;
			}

			vramRestoreMainBanks(vramtmp);
		}
		else renderer->lights[i]=0;
	}

	return 1;
}

uint16 CalcTextureExtent(int high,int low)
{
	return ((high-low)*10)>>3;
}

void SetFlatColor(Renderer *renderer,int col,uint8 dark)
{
	if(col>=128)
	{
		DSEnd();
		DSPolyFormat(DS_POLY_ALPHA(31)|DS_POLY_CULL_NONE);
		DSBegin(DS_QUADS);
		DSColor(renderer->palette[col]);
	}
	else
	{
		uint32 r=renderer->palette[col]&0x1f;
		uint32 g=(renderer->palette[col]>>5)&0x1f;
		uint32 b=(renderer->palette[col]>>10)&0x1f;
		uint32 l=renderer->lighttable[dark];
		DSColor3b(r*l>>12,g*l>>12,b*l>>12);
	}
}

void UnsetFlatColor(int col)
{
	if(col>=128)
	{
		DSEnd();
		DSPolyFormat(DS_POLY_ALPHA(31)|DS_POLY_CULL_NONE|DS_POLY_FOG);
		DSBegin(DS_QUADS);
	}
}

int AnimateTexture(int texture,int frame)
{
	if(texture==15) return texture+(frame&1);
	if(texture==16) return texture-(frame&1);
	return texture;
}

void RenderWall(Renderer *renderer,int tex,uint8 dark,uint16 u1,uint16 u2,int x1,int x2,int y1,int y2,int z1,int z2)
{
	DSSetTexture(renderer->textures[tex]);

	uint32 l=renderer->lighttable[dark]-1;
	DSColor3b(l>>7,l>>7,l>>7);
	DSTexCoord2t16(u1,0); DSVertex3v16(x1,z1,y1);
	DSTexCoord2t16(u2,0); DSVertex3v16(x1,z2,y1);
	DSTexCoord2t16(u2,2048); DSVertex3v16(x2,z2,y2);
	DSTexCoord2t16(u1,2048); DSVertex3v16(x2,z1,y2);

	if(renderer->lights[tex])
	{
		DSEnd();
		DSPolyFormat(DS_POLY_ALPHA(31)|DS_POLY_CULL_NONE|DS_POLY_DEPTH_TEST_EQUAL);
		DSBegin(DS_QUADS);

		DSSetTexture(renderer->lights[tex]);
		DSColor(0x7fff);
		DSSetPaletteOffset(512,DS_TEX_FORMAT_PAL8);

		DSTexCoord2t16(u1,0); DSVertex3v16(x1,z1,y1);
		DSTexCoord2t16(u2,0); DSVertex3v16(x1,z2,y1);
		DSTexCoord2t16(u2,2048); DSVertex3v16(x2,z2,y2);
		DSTexCoord2t16(u1,2048); DSVertex3v16(x2,z1,y2);

		DSSetPaletteOffset(0,DS_TEX_FORMAT_PAL8);

		DSEnd();
		DSPolyFormat(DS_POLY_ALPHA(31)|DS_POLY_CULL_NONE|DS_POLY_FOG);
		DSBegin(DS_QUADS);
	}
}

void PrintText(uint8 x,uint8 y,char *text)
{
	uint16 *vramptr=(uint16 *)0x6204000;
	while(*text)
	{
		vramptr[x+y*64]=*text*2;
		vramptr[x+y*64+32]=*text*2+1;
		text++;
		x++;
	}
}

char *FormatTime(int time)
{
	static char str[6];
	sprintf(str,time<250?" %d:%02d":"%02d:%02d",time/25,(time%25)*4);
	return str;
}

void Render(Renderer *renderer,Map *map,struct Game *game,int animticks)
{
	int zdist=24,xydist=zdist*4;
	int map_x=game->x>>12;
	int map_y=game->y>>12;
	int tile_x=game->x&0xfff;
	int tile_y=game->y&0xfff;
	int tile_z=game->z;

	if(tile_x<xydist && (tile_z<MapFloor(map,map_x-1,map_y)+zdist||tile_z>MapRoof(map,map_x-1,map_y)-zdist) ) tile_x=xydist;
	if(tile_y<xydist && (tile_z<MapFloor(map,map_x,map_y-1)+zdist||tile_z>MapRoof(map,map_x,map_y-1)-zdist) ) tile_y=xydist;
	if(tile_x>4095-xydist && (tile_z<MapFloor(map,map_x+1,map_y)+zdist||tile_z>MapRoof(map,map_x+1,map_y)-zdist) ) tile_x=4095-xydist;
	if(tile_y>4095-xydist && (tile_z<MapFloor(map,map_x,map_y+1)+zdist||tile_z>MapRoof(map,map_x,map_y+1)-zdist) ) tile_y=4095-xydist;

	if(tile_z<MapFloor(map,map_x,map_y)+zdist) tile_z=MapFloor(map,map_x,map_y)+zdist;
	if(tile_z>MapRoof(map,map_x,map_y)-zdist) tile_z=MapRoof(map,map_x,map_y)-zdist;

	DSMatrixMode(DS_PROJECTION);
	DSLoadIdentity();
	DSPerspective(75,256.0/192.0,0.01,10);

	DSMatrixMode(DS_MODELVIEW);
	DSLoadIdentity();
	DSRotateYi(game->a/2+128);
	//DSTranslate3f32(-game->x,-game->z*4,-game->y);
	DSTranslate3f32(-(map_x<<12)-tile_x,-tile_z*4,-(map_y<<12)-tile_y);
	//DSScale3f32(1<<22,1<<12,1<<22); //glScalef(1,1.0/1024.0,1);
	DSScalef(4096,4,4096); //glScalef(1,1.0/1024.0,1);

	DSSetControl(DS_ANTIALIAS|DS_TEXTURING|DS_FOG);
	DSPolyFormat(DS_POLY_ALPHA(31)|DS_POLY_CULL_NONE|DS_POLY_FOG);

	DSSetPaletteOffset(0,DS_TEX_FORMAT_PAL8);

	DSSetFogf(0,0,0,31,0.5,10,0.01,10);

	DSBegin(DS_QUADS);

	for(int y=0;y<map->h;y++)
	for(int x=0;x<map->w;x++)
	{
		MapTile *tile=&map->tiles[x+y*map->w];

		DSSetTexture(renderer->whitetexture);

		uint32 c=tile->darkness;

		if(game->z>=tile->floor && tile->floor<tile->roof)
		{
			SetFlatColor(renderer,tile->floorcol,c);
			DSVertex3v16(x,tile->floor,y);
			DSVertex3v16(x,tile->floor,y+1);
			DSVertex3v16(x+1,tile->floor,y+1);
			DSVertex3v16(x+1,tile->floor,y);
			UnsetFlatColor(tile->floorcol);
		}

		if(game->z<=tile->roof && tile->floor<tile->roof)
		{
			SetFlatColor(renderer,tile->roofcol,c);
			DSVertex3v16(x,tile->roof,y);
			DSVertex3v16(x,tile->roof,y+1);
			DSVertex3v16(x+1,tile->roof,y+1);
			DSVertex3v16(x+1,tile->roof,y);
			UnsetFlatColor(tile->roofcol);
		}

		if(x>0)
		{
			MapTile *prevtile=&map->tiles[(x-1)+y*map->w];
			if(tile->floor<tile->roof || prevtile->floor<prevtile->roof)
			{
				if(prevtile->roof!=tile->roof)
				{
					int low,high,actual;
					uint8 cw;
					if(prevtile->roof<tile->roof)
					{
						if(x>map_x) goto skip1;
						low=prevtile->roof; high=tile->roof; actual=prevtile->actualroof; cw=c;
					}
					else
					{
						if(x<=map_x) goto skip1;
						low=tile->roof; high=prevtile->roof; actual=tile->actualroof; cw=prevtile->darkness;
					}
					uint16 u=CalcTextureExtent(high,low);
					uint16 u0=CalcTextureExtent(low,actual);
					int tex=AnimateTexture(tile->textures[0],animticks);
					RenderWall(renderer,tex,cw,u0,u+u0,x,x,y,y+1,low,high);
				}

				skip1:

				if(prevtile->floor!=tile->floor)
				{
					int low,high;
					uint8 cw;
					if(prevtile->floor<tile->floor)
					{
						if(x<=map_x) goto skip2;
						low=prevtile->floor; high=tile->floor; cw=prevtile->darkness;
					}
					else
					{
						if(x>map_x) goto skip2;
						low=tile->floor; high=prevtile->floor; cw=c;
					}
					uint16 u=CalcTextureExtent(high,low);
					int tex=AnimateTexture(tile->textures[2],animticks);
					RenderWall(renderer,tex,cw,u,0,x,x,y,y+1,low,high);
				}

				skip2: 0;
			}
		}

		if(y>0)
		{
			MapTile *prevtile=&map->tiles[x+(y-1)*map->w];

			if(tile->floor<tile->roof || prevtile->floor<prevtile->roof)
			{
				if(prevtile->roof!=tile->roof)
				{
					int low,high,actual;
					uint8 cw;
					if(prevtile->roof<tile->roof)
					{
						if(y>map_y) goto skip3;
						low=prevtile->roof; high=tile->roof; actual=prevtile->actualroof; cw=c;
					}
					else
					{
						if(y<=map_y) goto skip3;
						low=tile->roof; high=prevtile->roof; actual=tile->actualroof; cw=prevtile->darkness;
					}
					uint16 u=CalcTextureExtent(high,low);
					uint16 u0=CalcTextureExtent(low,actual);
					int tex=AnimateTexture(tile->textures[1],animticks);
					RenderWall(renderer,tex,cw,u0,u+u0,x,x+1,y,y,low,high);
				}

				skip3:

				if(prevtile->floor!=tile->floor)
				{
					int low,high;
					uint8 cw;
					if(prevtile->floor<tile->floor)
					{
						if(y<=map_y) goto skip4;
						low=prevtile->floor; high=tile->floor; cw=prevtile->darkness;
					}
					else
					{
						if(y>map_y) goto skip4;
						low=tile->floor; high=prevtile->floor; cw=c;
					}
					uint16 u=CalcTextureExtent(high,low);
					int tex=AnimateTexture(tile->textures[3],animticks);
					RenderWall(renderer,tex,cw,u,0,x,x+1,y,y,low,high);
				}

				skip4: 0;
			}
		}
	}

	char *message,msgbuf[31];
	if(game->eventtype==GameLapEvent&&game->frame-game->eventtime<=200)
	{
		sprintf(msgbuf,"  \207\210 Full lap time  %s \207\210  ",FormatTime(game->lasttime));
		message=msgbuf;
	}
	else if(game->eventtype==GameGateEvent&&game->frame-game->eventtime<=100)
	{
		sprintf(msgbuf,"      \205\206 Go to gate %d \205\206      ",game->gate%4+1);
		message=msgbuf;
	}
	else
	{
		int num_messages=sizeof(messages)/sizeof(messages[0]);
		int n=(game->frame/100)%num_messages;
		message=messages[n];
	}

	char gatestr[]="\226\227 1";
	char timestr[]=" -:-- \224\225";
	if(game->gate)
	{
		sprintf(gatestr,"\226\227 %d",game->gate%4+1);
		sprintf(timestr,"%s \224\225",FormatTime(game->timer));
	}
	PrintText(1,1,gatestr);
	PrintText(23,1,timestr);

	if(game->lasttime)
	{
		char lapstr[12];
		sprintf(lapstr,"\207\210 %s \207\210",FormatTime(game->lasttime));
		PrintText(9,1,lapstr);
	}

	PrintText(1+32,2,message);

	int highlight=DSf32(expf(-(game->frame-game->lasthit)/12.0));
	SetHighlightBar(highlight);

	DSEnd();

	DSFlush(DS_FLUSH_NO_SORTING);
}
